#pragma once
#include "stdafx.h"

#include "common/meshGeo.h"
#include "common/gametimer.h"
#include "common/renderitem.h"
#include "common/material.h"
#include "frameresource.h"
#include "inithelper.h"

class Renderer
{
    friend class InitHelper;
    friend class ModelLoader;
public:
    Renderer(UINT width, UINT height, std::wstring name = L"DX12_APP");
    ~Renderer();

    void OnInit();
    void OnUpdate();
    void OnRender();
    void OnDestroy();

    void OnKeyDown(UINT8 /*key*/);
    void OnKeyUp(UINT8 /*key*/);
    void OnMouseDown(WPARAM, int, int);
    void OnMouseUp(WPARAM, int, int);
    void OnMouseMove(WPARAM, int, int);

    UINT GetWidth() const { return m_width; }
    UINT GetHeight() const { return m_height; }
    const WCHAR* GetTitle() const { return m_title.c_str(); }

    void SetCustomWindowText(LPCWSTR text);

    std::unique_ptr<GameTimer> m_timer;
    bool isPaused() const { return m_isPaused; }
    void CalculateFrameStats();

private:
    // frames in swapchain
    static const UINT FrameCount = 2;
    UINT m_frameIndex = 0;

    // frame resources
    static const int NumFrameResource = 3;
    std::vector<std::unique_ptr<FrameResource>> m_frameResources;
    UINT m_currFrameResourceIndex = 0;

    // render items
    std::vector<std::unique_ptr<RenderItem>> m_allRitems;
    std::vector<RenderItem*> m_opaqueRitems;
    std::vector<RenderItem*> m_transparentRitems;

    //main pass cbuffer
    PassConstants m_mainPassCB;

    bool m_isPaused = false;

    // Viewport dimensions.
    UINT m_width;
    UINT m_height;
    float m_aspectRatio;
    // Adapter info.
    bool m_useWarpDevice = false;
    
    //render component
    CD3DX12_VIEWPORT m_viewport;
    CD3DX12_RECT m_scissorRect;
    ComPtr<ID3D12Device> m_device;
   
    ComPtr<ID3D12CommandQueue> m_commandQueue;
    ComPtr<ID3D12CommandAllocator> m_commandAlloc;
    ComPtr<ID3D12GraphicsCommandList> m_commandList;
    ComPtr<IDXGISwapChain4> m_swapChain;
    ComPtr<ID3D12Resource> m_renderTargets[FrameCount];
    ComPtr<ID3D12Resource> m_depthStencilBuffer;
    ComPtr<ID3D12RootSignature> m_rootSignature;

    ComPtr<ID3D12PipelineState> m_pso; 
    ComPtr<ID3D12PipelineState> m_pso_trans;

    ComPtr<ID3D12DescriptorHeap> m_rtvHeap;
    ComPtr<ID3D12DescriptorHeap> m_srvHeap;
    ComPtr<ID3D12DescriptorHeap> m_dsvHeap;
    UINT m_rtvDescriptorSize = 0;
    UINT m_dsvDescriptorSize = 0;
    UINT m_srvDescriptorSize = 0;
    UINT m_passCBOffset = 0;
    UINT m_texOffset = 0;
    UINT m_sampleOffset = 0;
    
    std::unordered_map<std::string, std::unique_ptr<MeshGeometry>> m_meshes;
    std::unordered_map<std::string, std::unique_ptr<Material>> m_materials;
    std::vector<std::unique_ptr<Texture>> m_textures;
    /*
        Constant buffer view (CBV)
        Unordered access view (UAV)
        Shader resource view (SRV)
        Samplers
        Render Target View (RTV)
        Depth Stencil View (DSV)
        Index Buffer View (IBV)
        Vertex Buffer View (VBV)
        Stream Output View (SOV)
    */
    
    //sync
    HANDLE m_fenceEvent;
    ComPtr<ID3D12Fence> m_fence;
    UINT m_fenceValue = 1;

    // Root assets path.
    std::wstring m_assetsPath;
    // Window title.
    std::wstring m_title;

    // cam param
    float m_phi = 0.4f * M_PI;
    float m_theta = 1.5f * M_PI;
    float m_radius = 20.0f;
    int ms_x, ms_y;
    XMFLOAT4X4 m_view, m_proj;
    XMFLOAT3 m_eyePos;


    void PopulateCommandList();
    void FlushCommandQueue();

    void UpdateCamera();
    void UpdateObjectCBs();
    void UpdateMainPassCB();
    void UpdateMaterialCBs();

    std::array<const CD3DX12_STATIC_SAMPLER_DESC, 6> GetStaticSamplers();
};

